package com.googlecode.vmock.matchers;

import com.googlecode.vmock.utils.ArrayUtils;
import com.googlecode.vmock.utils.Validate;

/**
 * Matcher made specifically for varargs parameters of mocked calls.
 * 
 * @author marcin.jagus
 */
public class CompositeMatcher implements Matcher {

    /**
     * Array of matchers aggregated in this matcher.
     */
    private Matcher[] matchers;

    /**
     * Creates new composite matcher which will aggregate a given array of matchers.
     * 
     * @param matchers Array of matchers. Can be empty. Cannot be <code>null</code>.
     */
    public CompositeMatcher(Matcher[] matchers) {
        Validate.notNull(matchers);
        this.matchers = matchers;
    }

    /**
     * Matches a given value only if all aggregated matchers of this composite matcher match it.
     */
    @Override
    public boolean matches(Object value) {
        if (!value.getClass().isArray()) {
            return false;
        }

        Object[] params = (Object[]) value;

        if (params.length != matchers.length) {
            return false;
        }

        for (int i = 0; i < params.length; i++) {
            if (!matchers[i].matches(params[i])) {
                return false;
            }
        }

        return true;
    }

    /**
     * Compares this composite matcher to another matcher, which must be a composite matcher too.
     * <p>
     * Composite matcher has a higher priority over another composite matcher if it aggregates less matchers or sum of aggregated matcher's priorities
     * is lower than the same sum of compared composite matcher.
     */
    @Override
    public int compareTo(Matcher o) {
        if (!(o instanceof CompositeMatcher)) {
            throw new IllegalArgumentException("Cannot compare CompositeMatcher against matcher that is not a CompositeMatcher");
        }

        CompositeMatcher matcher = (CompositeMatcher) o;
        int compareResult = this.matchers.length - matcher.matchers.length;

        if (compareResult == 0) {
            Matcher[] myMatchers = this.matchers;
            Matcher[] theirMatchers = matcher.matchers;

            for (int i = 0; i < myMatchers.length; i++) {
                int partialComparsion = myMatchers[i].compareTo(theirMatchers[i]);
                if (partialComparsion < 0) {
                    compareResult--;
                } else if (partialComparsion > 0) {
                    compareResult++;
                }
            }
        }

        return compareResult;
    }

    @Override
    public String toString() {
        return String.format("CompositeMatcher(%s)", ArrayUtils.toString(matchers));
    }
}
